package edu.neu.cs6515.turn;

import java.util.List;

import com.google.common.collect.ImmutableList;
import com.google.java.contract.Ensures;
import com.google.java.contract.Requires;

import edu.neu.cs6515.admin.Administrator;
import edu.neu.cs6515.admin.TileBag;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.xml.Score;
import edu.neu.cs6515.xml.Tile;

/**
 * The proxy object is passed on to a player whenever the player is supposed to
 * make a placement.
 * 
 * TEMPORAL LOGIC / ORDER OF METHOD CALLS: (Using regex syntax)
 * 
 * Call anytime: --------------------------------------- getRequiredPlacements()
 * getAllTilesInPlayersHand()
 * 
 * Call in the specified order: --------------------------------------- (Order
 * in first brace is irrelevant) (getCurrentBoardState()+ getCurrentScore()+
 * getAllTilesInPlayersHand()+ getRequiredPlacements()+ placeTile())+
 * 
 */
public class TurnProxy extends AbstractTurnProxy {

    final TileBag tileBag;

    /**
     * Initializes a proxy for exactly one turn
     * 
     * @param board
     *            the board used in this turn
     * @param tilesInPlayersHand
     *            the tiles that are available for the player
     * @param score
     *            the score of the player before the turn
     * @param tileBag
     *            the tiles that can be handed out to the player
     */
    @Requires({ "board != null", "tilesInPlayersHand != null", "tileBag != null",
            "tilesInPlayersHand.size() <= 6", "score != null" })
    @Ensures("getRequiredPlacements() == 1")
    public TurnProxy(final HexagonBoard board,
            final List<Tile> tilesInPlayersHand, final Score score,
            final TileBag tileBag) {
        super(board, tilesInPlayersHand, score);
        this.tileBag = tileBag;
    }

    public TurnProxy(final HexagonBoard board,
            final List<Tile> tilesInPlayersHand, final Score score,
            final List<Tile> tiles) {
        this(board, tilesInPlayersHand, score, new TileBag(tiles));
    }

    /**
     * Used to return a new Tile to the player that just made a legal placment
     * 
     * @return List of Tiles of length 1 if the admin has tiles, empty list
     *         otherwise
     */
    @Override
    protected List<Tile> getNewTile() {
        if (tileBag.getNumberOfTiles() > 0) {
            Tile tileToReturn = tileBag.drawATile();
            tilesInPlayersHand.add(tileToReturn);
            return ImmutableList.of(tileToReturn);
        } else {
            return ImmutableList.of();
        }
    }

    @Override
    public boolean isReRackable() {
        return !this.isReRacked() &&
                !isAnyLowestScoredColorInHand() &&
                this.getRequiredPlacements() == 0 &&
                tileBag.getNumberOfTiles() >= 6;
    }

    @Requires({ "isReRackable()" })
    @Override
    public void reRack() {
        List<Tile> tiles = this.tileBag.drawTiles(Administrator.INITIAL_TILES_IN_HAND);
        this.tileBag.depositTiles(this.tilesInPlayersHand);
        this.tilesInPlayersHand = tiles;
        this.reRacked = true;
    }
}
